package com.ld.common.plugin;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import com.ld.shieldsb.common.core.collections.ListUtils;
import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.reflect.ClassUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PluginManager {
    // 插件map
    private static Map<String, List<Plugin>> pluginMap = new HashMap<>();
    // 扫描的目录
    private static List<String> packageNames = null;
    private static int loadNum = 0;
    private static final Map<String, String> PKG_FILTER_MAP = new HashMap<>(); // 包名过滤设置，key为包名，value为此包名筛选的包名如ld.com（key）下值过滤包名中含有task（value）的目录
    /**
     * 多线程并行处理定时任务时，Timer运行多个TimeTask时，只要其中之一没有捕获抛出的异常，其它任务便会自动终止运行，使用ScheduledExecutorService则没有这个问题
     * org.apache.commons.lang3.concurrent.BasicThreadFactory
     */
    private static ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = new ScheduledThreadPoolExecutor(10,
            new BasicThreadFactory.Builder().namingPattern("initPluginM-schedule-pool-%d").daemon(true).build());

    /**
     * 获取所有的插件
     *
     * @Title getPlugins
     * @author 吕凯
     * @date 2019年2月25日 上午10:39:05
     * @return List<Plugin>
     */
    public static List<Plugin> getPlugins() {
        List<Plugin> allPlugins = new ArrayList<>();
        Set<String> keys = pluginMap.keySet();
        if (ListUtils.isNotEmpty(keys)) {
            for (String key : keys) {
                allPlugins.addAll(pluginMap.get(key));
            }
        }
        return allPlugins;
    }

    /**
     * 获取命名空间（一般为包名）下的插件
     *
     * @Title getPlugins
     * @author 吕凯
     * @date 2019年2月25日 上午10:38:40
     * @param nameSpace
     * @return List<Plugin>
     */
    public static List<Plugin> getPlugins(String nameSpace) {
        return pluginMap.get(nameSpace);
    }

    public static List<String> getPackageName() {
        return packageNames;
    }

    /**
     * 初始化
     *
     * @Title init
     * @author 吕凯
     * @date 2019年2月25日 上午9:42:07
     * @param packageName
     *            void
     */
    public static void init() {
        packageNames = getPkgNamesSet();
        reloadPlugins();
    }

    /**
     * 重新加载插件
     *
     * @Title reloadPlugins
     * @author 吕凯
     * @date 2019年2月25日 下午1:43:03 void
     */
    public static void reloadPlugins() {
        loadNum = 0;
        if (ListUtils.isNotEmpty(packageNames)) {
            synchronized (packageNames) {
                for (String packageName : packageNames) {
                    pluginMap.remove(packageName);
                    List<Plugin> plugins = getPlugin(packageName);
                    pluginMap.put(packageName, plugins);
                }
            }
            log.warn("插件加载完成……,共发现插件 " + getPlugins().size() + " 个,加载" + loadNum + "个！");
        } else {
            log.warn("未定义扫描主目录，请先调用init()方法初始化！");
        }
    }

    /**
     *
     * 加载指定目录下的插件
     *
     * @Title loadExtendsPlugins
     * @author 吕凯
     * @date 2019年3月5日 下午5:22:45
     * @param packageName
     *            扫描的包 void
     */
    public static void loadExtendsPlugins(String packageName) {
        loadExtendsPlugins(packageName, null);
    }

    /**
     * 加载指定目录下的插件
     *
     * @Title loadExtendsPlugins
     * @author 吕凯
     * @date 2019年2月25日 上午10:29:20
     * @param packageName
     *            扫描的包
     * @param pkgFilter
     *            扫描的包中过滤的目录名称（只扫描目录中包含这个字符的） void
     */
    public static void loadExtendsPlugins(String packageName, String pkgFilter) {
        if (packageNames == null) {
            packageNames = new ArrayList<>();
        }
        if (packageName != null) {
            pluginMap.remove(packageName);
            packageNames.remove(packageName); // 移除再添加
            packageNames.add(packageName);
            PKG_FILTER_MAP.put(packageName, pkgFilter);
            List<Plugin> plugins = getPlugin(packageName);
            pluginMap.put(packageName, plugins);
        } else {
            log.warn("未定义扫描目录！");
        }
    }

    /**
     * 返回包下的插件
     *
     * @Title loadPlugin
     * @author 吕凯
     * @date 2019年2月25日 下午4:16:45
     * @param packageName
     * @return List<Plugin>
     */
    private static List<Plugin> getPlugin(String packageName) {
        List<Plugin> plugins = new ArrayList<>();
        List<Class<? extends Plugin>> classList = ClassUtil.getClassList(packageName, PKG_FILTER_MAP.get(packageName), Plugin.class, true); // 获取Plugin的子类，包名中必须包含plugin
        for (Class<? extends Plugin> pluginCls : classList) {
            Plugin plugin = loadPlugin(pluginCls);
            plugins.add(plugin);
        }
        return plugins;
    }

    public static Plugin loadPlugin(Class<? extends Plugin> pluginCls) {
        Plugin plugin = null;
        try {
            Plugin pluginLoad = pluginCls.newInstance();
            plugin = pluginLoad;
            Map<String, String> maps = PluginUtil.getPreferenceMap();
            String loadInfo = null;
            if (maps != null && pluginLoad.getPluginInfo() != null) {
                loadInfo = maps.get(pluginLoad.getPluginInfo().getClassName());
            }
            if (loadInfo == null || loadInfo.equals("true")) {
                loadNum++;
                SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
                    pluginLoad.initializePlugin();
                    // 更新加载时间
                    pluginLoad.getPluginInfo().setLoadTime(new Date());
                    log.warn("加载插件（异步）完成： " + pluginCls.getName());
                }, 100, TimeUnit.MILLISECONDS);

                log.warn("加载插件（异步）： " + pluginCls.getName());
            } else {
                log.warn("插件【未启用】： " + pluginCls.getName());
            }
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("", e);
        }
        return plugin;
    }

    /**
     * 销毁
     *
     * @Title destroy
     * @author 吕凯
     * @date 2019年2月25日 上午9:42:17 void
     */
    public static void destroy() {
        List<Plugin> plugins = getPlugins();
        if (ListUtils.isNotEmpty(plugins)) {
            for (Plugin plugin : plugins) {
                plugin.destroyPlugin();
            }
            plugins.clear();
        }
        if (ListUtils.isNotEmpty(packageNames)) {
            synchronized (packageNames) {
                packageNames.clear();
            }
        }
    }

    /**
     * 获取配置的包名
     *
     * @Title getPkgNamesSet
     * @author 吕凯
     * @date 2019年3月5日 下午4:47:47
     * @return List<String>
     */
    public static List<String> getPkgNamesSet() {
        List<String> pkgs = PropertiesModel.PROJECT.getStringList("application.plugin.scanpkg");
        List<String> pkgFilters = PropertiesModel.PROJECT.getStringList("application.plugin.scanpkg.pkgfilter");
        PKG_FILTER_MAP.clear();
        if (ListUtils.isNotEmpty(pkgs)) {
            for (int i = 0; i < pkgs.size(); i++) {
                String pkgName = pkgs.get(i);
                String pkgFilter = null;
                if (ListUtils.isNotEmpty(pkgFilters) && pkgFilters.size() > i) {
                    pkgFilter = pkgFilters.get(i);
                }
                PKG_FILTER_MAP.put(pkgName, pkgFilter);
            }
        }
        return pkgs;
    }

    /**
     * 获取包及包过滤设置的Map
     *
     * @Title getPkgFilterMap
     * @author 吕凯
     * @date 2019年3月5日 下午5:10:28
     * @return Map<String,String>
     */
    public static Map<String, String> getPkgFilterMap() {
        return PKG_FILTER_MAP;
    }

    /**
     * 设置插件类的扫描包及包过滤
     *
     * @Title setPkgNamesSet
     * @author 吕凯
     * @date 2019年3月6日 下午2:52:56
     * @param scanpkg
     * @param pkgfilter
     * @return boolean
     */
    public static boolean setPkgNamesSet(String scanpkg, String pkgfilter) {
        PropertiesModel.PROJECT.save("application.plugin.scanpkg", scanpkg);
        PropertiesModel.PROJECT.save("application.plugin.scanpkg.pkgfilter", pkgfilter);
        return true;
    }

}
